001    /*
002     * Copyright 2004 Niclas Hedhman
003     * Copyright 2004-2006 Stephen J. McConnell
004     *
005     * Licensed  under the  Apache License,  Version 2.0  (the "License");
006     * you may not use  this file  except in  compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *   http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed  under the  License is distributed on an "AS IS" BASIS,
013     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
014     * implied.
015     *
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    package net.dpml.transit.artifact;
021    
022    import java.io.InputStream;
023    import java.io.IOException;
024    import java.io.OutputStream;
025    import java.io.File;
026    import java.net.UnknownServiceException;
027    import java.net.URI;
028    import java.net.URL;
029    import java.net.URLConnection;
030    import java.net.URISyntaxException;
031    
032    import net.dpml.transit.Artifact;
033    import net.dpml.transit.Transit;
034    import net.dpml.transit.SecuredTransitContext;
035    import net.dpml.transit.CacheHandler;
036    
037    import net.dpml.lang.Part;
038    
039    import net.dpml.util.MimeTypeHandler;
040    
041    /**
042     * The connection handler for URLs based on the "artifact" protocol family.
043     * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a>
044     * @version 1.0.0
045     */
046    public class ArtifactURLConnection extends URLConnection
047    {
048        // ------------------------------------------------------------------------
049        // state
050        // ------------------------------------------------------------------------
051    
052       /**
053        * Transit context.
054        */
055        private final SecuredTransitContext m_context;
056    
057       /**
058        * Artifact.
059        */
060        private final Artifact m_artifact;
061    
062       /**
063        * Reference fragment.
064        */
065        private final String m_reference;
066    
067       /**
068        * The connected state.
069        */
070        private boolean m_connected;
071    
072        // ------------------------------------------------------------------------
073        // constructor
074        // ------------------------------------------------------------------------
075    
076        /**
077         * Creation of a new handler.
078         * @param url the url to establish a connection with
079         * @param context the transit context
080         * @exception NullPointerException if the url argument is null
081         * @exception IOException if the url argument is not a valid,
082         *            i.e. the path must contain a group and an artifactId
083         *            separated by a slash.
084         */
085        ArtifactURLConnection( URL url, SecuredTransitContext context )
086            throws NullPointerException, IOException
087        {
088            super( url );
089    
090            Transit.getInstance(); // make sure Transit is initialized
091    
092            m_context = context;
093            m_reference = getReference( url );
094    
095            String spec = getRealSpec( url, m_reference );
096            try
097            {
098                m_artifact = Artifact.createArtifact( spec );
099            }
100            catch( URISyntaxException e )
101            {
102                throw new IOException( e.toString() );
103            }
104        }
105    
106        // ------------------------------------------------------------------------
107        // URLConnection
108        // ------------------------------------------------------------------------
109    
110       /**
111        * Establish a connection.  The implementation will attempt to
112        * resolve the resource relative to the cache and associated hosts.
113        *
114        * @exception IOException is an error occurs while attempting to establish
115        *  the connection.
116        */
117        public void connect()
118            throws IOException
119        {
120            m_connected = true;
121        }
122    
123       /**
124        * Return an input stream to the resource.
125        * @return the input stream
126        * @exception IOException is an error occurs
127        */
128        public InputStream getInputStream()
129            throws IOException
130        {
131            connect();
132            CacheHandler cache = m_context.getCacheHandler();
133            if( null != m_reference )
134            {
135                return cache.getResource( m_artifact, m_reference );
136            }
137            else
138            {
139                return cache.getResource( m_artifact );
140            }
141        }
142    
143       /**
144        * Return an output stream to the resource.
145        * @return the output stream
146        * @exception IOException if any I/O problems occur.
147        */
148        public OutputStream getOutputStream()
149            throws IOException
150        {
151            CacheHandler cache = m_context.getCacheHandler();
152            return cache.createOutputStream( m_artifact );
153        }
154    
155       /**
156        * Reutrn the mimetype of the content.
157        * @return the content mimetype
158        */
159        public String getContentType()
160        {
161            String type = m_artifact.getType();
162            return MimeTypeHandler.getMimeType( type );
163        }
164    
165       /**
166        * Return the content for this artifact.
167        * @return the content object (possibly null)
168        * @exception IOException is an error occurs
169        */
170        public Object getContent()
171            throws IOException
172        {
173            Object content = getContent( new Class[0] );
174            if( content != null )
175            {
176                return content;
177            }
178            else
179            {
180                return super.getContent();
181            }
182        }
183    
184       /**
185        * Return the content for this artifact.
186        * @param classes a sequence of classes against which the
187        *   implementation will attempt to establish a known match
188        * @return the content object (possibly null)
189        * @exception IOException is an error occurs
190        */
191        public Object getContent( Class[] classes )
192            throws IOException
193        {
194            String type = m_artifact.getType();
195    
196            // 
197            // if the type is a plugin then handle this directly
198            //
199    
200            if( "part".equals( type ) )
201            { 
202                URI uri = m_artifact.toURI();
203                Part part = Part.load( uri );
204                return part.getContent( classes );
205            }
206            
207            //
208            // otherwise fallback on the default jvm content handling
209            //
210    
211            try
212            {
213                Object content = super.getContent( classes );
214                if( content != null )
215                {
216                    return content;
217                }
218            }
219            catch( UnknownServiceException use )
220            {
221                boolean ignoreThis = true;
222            }
223    
224            //
225            // attempt to resolve this locally as we may be dealing
226            // with Magic references to the artifact File
227            //
228    
229            for( int i=0; i < classes.length; i++ )
230            {
231                Class c = classes[i];
232                if( c.equals( File.class ) )
233                {
234                    return m_context.getCacheHandler().getLocalFile( m_artifact );
235                }
236            }
237            return null;
238        }
239        
240        // ------------------------------------------------------------------------
241        // implementation
242        // ------------------------------------------------------------------------
243    
244       /**
245        * Return a fragment referencing content within the resource referenced by
246        * the artifact.
247        * @param url the url
248        * @return the fragment or null if this is not a referential url
249        */
250        private String getReference( URL url )
251        {
252            String path = url.getPath();
253            int i = path.indexOf( '!' );
254            if( i < 0 )
255            {
256                return null;
257            }
258            else
259            {
260                return path.substring( i );
261            }
262        }
263    
264       /**
265        * Return the real specification of the supplied url.
266        * @param url the url to evaluate
267        * @param ref a reference fragment
268        * @return the artifact url spec withough the ref fragment
269        */
270        private String getRealSpec( URL url, String ref )
271        {
272            if( null != ref )
273            {
274                String spec = url.toString();
275                int j = spec.indexOf( ref );
276                if( j > 0 )
277                {
278                    String s = spec.substring( 0, j );
279                    String version = url.getUserInfo();
280                    if( null != version )
281                    {
282                        s = s + "#" + version;
283                    }
284                    return s;
285                }
286            }
287            return url.toString();
288        }
289    }